home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Capture / AudioCap / AudioCapDlg.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  30.0 KB  |  1,057 lines

  1. //------------------------------------------------------------------------------
  2. // File: AudioCapDlg.cpp
  3. //
  4. // Desc: DirectShow sample code - Implementation of CAudioCapDlg class for
  5. //       audio capture sample application.
  6. //
  7. // Copyright (c) 2000-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10. #include "stdafx.h"
  11. #include "AudioCap.h"
  12. #include "AudioCapDlg.h"
  13. #include <atlbase.h>
  14.  
  15. #include <mtype.h>
  16. #include "mfcutil.h"
  17. #include "dshowutil.h"
  18.  
  19. #ifdef _DEBUG
  20. #define new DEBUG_NEW
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24.  
  25.  
  26. /////////////////////////////////////////////////////////////////////////////
  27. // CAboutDlg dialog used for App About
  28.  
  29. class CAboutDlg : public CDialog
  30. {
  31. public:
  32.     CAboutDlg();
  33.  
  34. // Dialog Data
  35.     //{{AFX_DATA(CAboutDlg)
  36.     enum { IDD = IDD_ABOUTBOX };
  37.     //}}AFX_DATA
  38.  
  39.     // ClassWizard generated virtual function overrides
  40.     //{{AFX_VIRTUAL(CAboutDlg)
  41.     protected:
  42.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  43.     //}}AFX_VIRTUAL
  44.  
  45. // Implementation
  46. protected:
  47.     //{{AFX_MSG(CAboutDlg)
  48.     //}}AFX_MSG
  49.     DECLARE_MESSAGE_MAP()
  50. };
  51.  
  52. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  53. {
  54.     //{{AFX_DATA_INIT(CAboutDlg)
  55.     //}}AFX_DATA_INIT
  56. }
  57.  
  58. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  59. {
  60.     CDialog::DoDataExchange(pDX);
  61.     //{{AFX_DATA_MAP(CAboutDlg)
  62.     //}}AFX_DATA_MAP
  63. }
  64.  
  65. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  66.     //{{AFX_MSG_MAP(CAboutDlg)
  67.         // No message handlers
  68.     //}}AFX_MSG_MAP
  69. END_MESSAGE_MAP()
  70.  
  71. /////////////////////////////////////////////////////////////////////////////
  72. // CAudioCapDlg dialog
  73.  
  74. CAudioCapDlg::CAudioCapDlg(CWnd* pParent /*=NULL*/)
  75.     : CDialog(CAudioCapDlg::IDD, pParent)
  76. {
  77.     //{{AFX_DATA_INIT(CAudioCapDlg)
  78.     //}}AFX_DATA_INIT
  79.     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  80.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  81. }
  82.  
  83. void CAudioCapDlg::DoDataExchange(CDataExchange* pDX)
  84. {
  85.     CDialog::DoDataExchange(pDX);
  86.     //{{AFX_DATA_MAP(CAudioCapDlg)
  87.     DDX_Control(pDX, IDC_STATIC_STATUS, m_strStatus);
  88.     DDX_Control(pDX, IDC_RADIO_MONO, m_btnMono);
  89.     DDX_Control(pDX, IDC_RADIO_8BIT, m_btn8BIT);
  90.     DDX_Control(pDX, IDC_RADIO_11KHZ, m_btn11KHZ);
  91.     DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause);
  92.     DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
  93.     DDX_Control(pDX, IDC_BUTTON_PLAY, m_btnPlay);
  94.     DDX_Control(pDX, IDC_LIST_FILTER_OUTPUTS, m_ListFilterOutputs);
  95.     DDX_Control(pDX, IDC_LIST_FILTER_INPUTS, m_ListFilterInputs);
  96.     DDX_Control(pDX, IDC_BUTTON_RECORD, m_btnRecord);
  97.     DDX_Control(pDX, IDC_BUTTON_PROPERTIES, m_btnProperties);
  98.     DDX_Control(pDX, IDC_LIST_INPUT_DEVICES, m_ListInputs);
  99.     DDX_Control(pDX, IDC_LIST_INPUT_PINS, m_ListInputPins);
  100.     DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
  101.     DDX_Control(pDX, IDC_EDIT_FILENAME, m_StrFilename);
  102.     DDX_Control(pDX, IDC_CHECK_WRITE, m_CheckWriteFile);
  103.     DDX_Control(pDX, IDC_CHECK_AUDITION, m_CheckAudition);
  104.     //}}AFX_DATA_MAP
  105. }
  106.  
  107. BEGIN_MESSAGE_MAP(CAudioCapDlg, CDialog)
  108.     //{{AFX_MSG_MAP(CAudioCapDlg)
  109.     ON_WM_SYSCOMMAND()
  110.     ON_WM_PAINT()
  111.     ON_WM_QUERYDRAGICON()
  112.     ON_WM_CLOSE()
  113.     ON_WM_DESTROY()
  114.     ON_LBN_SELCHANGE(IDC_LIST_INPUT_DEVICES, OnSelchangeListInputDevices)
  115.     ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
  116.     ON_LBN_SELCHANGE(IDC_LIST_INPUT_PINS, OnSelchangeListInputPins)
  117.     ON_BN_CLICKED(IDC_BUTTON_PROPERTIES, OnButtonProperties)
  118.     ON_BN_CLICKED(IDC_BUTTON_PLAY, OnButtonPlay)
  119.     ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnButtonPause)
  120.     ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
  121.     ON_BN_CLICKED(IDC_BUTTON_RECORD, OnButtonRecord)
  122.     //}}AFX_MSG_MAP
  123. END_MESSAGE_MAP()
  124.  
  125. /////////////////////////////////////////////////////////////////////////////
  126. // CAudioCapDlg message handlers
  127.  
  128. void CAudioCapDlg::OnSysCommand(UINT nID, LPARAM lParam)
  129. {
  130.     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  131.     {
  132.         CAboutDlg dlgAbout;
  133.         dlgAbout.DoModal();
  134.     }
  135.     else
  136.     {
  137.         CDialog::OnSysCommand(nID, lParam);
  138.     }
  139. }
  140.  
  141. // If you add a minimize button to your dialog, you will need the code below
  142. //  to draw the icon.  For MFC applications using the document/view model,
  143. //  this is automatically done for you by the framework.
  144.  
  145. void CAudioCapDlg::OnPaint() 
  146. {
  147.     if (IsIconic())
  148.     {
  149.         CPaintDC dc(this); // device context for painting
  150.  
  151.         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  152.  
  153.         // Center icon in client rectangle
  154.         int cxIcon = GetSystemMetrics(SM_CXICON);
  155.         int cyIcon = GetSystemMetrics(SM_CYICON);
  156.         CRect rect;
  157.         GetClientRect(&rect);
  158.         int x = (rect.Width() - cxIcon + 1) / 2;
  159.         int y = (rect.Height() - cyIcon + 1) / 2;
  160.  
  161.         // Draw the icon
  162.         dc.DrawIcon(x, y, m_hIcon);
  163.     }
  164.     else
  165.     {
  166.         CDialog::OnPaint();
  167.     }
  168. }
  169.  
  170. // The system calls this to obtain the cursor to display while the user drags
  171. //  the minimized window.
  172. HCURSOR CAudioCapDlg::OnQueryDragIcon()
  173. {
  174.     return (HCURSOR) m_hIcon;
  175. }
  176.  
  177. BOOL CAudioCapDlg::OnInitDialog()
  178. {
  179.     CDialog::OnInitDialog();
  180.  
  181.     // Add "About..." menu item to system menu.
  182.  
  183.     // IDM_ABOUTBOX must be in the system command range.
  184.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  185.     ASSERT(IDM_ABOUTBOX < 0xF000);
  186.  
  187.     CMenu* pSysMenu = GetSystemMenu(FALSE);
  188.     if (pSysMenu != NULL)
  189.     {
  190.         CString strAboutMenu;
  191.         strAboutMenu.LoadString(IDS_ABOUTBOX);
  192.         if (!strAboutMenu.IsEmpty())
  193.         {
  194.             pSysMenu->AppendMenu(MF_SEPARATOR);
  195.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  196.         }
  197.     }
  198.  
  199.     // Set the icon for this dialog.  The framework does this automatically
  200.     //  when the application's main window is not a dialog
  201.     SetIcon(m_hIcon, TRUE);            // Set big icon
  202.     SetIcon(m_hIcon, FALSE);        // Set small icon
  203.     
  204.     ////////////////////////////////////////////////////////////////////////
  205.     //
  206.     //  DirectShow-specific initialization code
  207.  
  208.     CoInitialize(NULL);
  209.  
  210.     if (FAILED(InitializeCapture()))
  211.     {
  212.         ReleaseCapture();
  213.         return FALSE;
  214.     }
  215.  
  216.     CheckDlgButton(IDC_RADIO_16BIT, TRUE);
  217.     CheckDlgButton(IDC_RADIO_STEREO, TRUE);
  218.     CheckDlgButton(IDC_RADIO_44KHZ, TRUE);
  219.  
  220.     return TRUE;  // return TRUE  unless you set the focus to a control
  221. }
  222.  
  223. HRESULT CAudioCapDlg::InitializeCapture()
  224. {
  225.     HRESULT hr;
  226.  
  227.     SetDefaults();
  228.     
  229.     if (FAILED(hr = GetInterfaces()))
  230.         return hr;
  231.  
  232.     if (FAILED(hr = FillLists()))
  233.         return hr;
  234.  
  235.     return hr;
  236. }
  237.  
  238. HRESULT CAudioCapDlg::FillLists() 
  239. {
  240.     HRESULT hr;
  241.     
  242.     // Enumerate and display the audio input devices installed in the system
  243.     hr = EnumFiltersWithMonikerToList(NULL, &CLSID_AudioInputDeviceCategory, m_ListInputs);
  244.     if (FAILED(hr))
  245.         return hr;
  246.  
  247.     // Select the first audio device in the list for the default
  248.     m_ListInputs.SetCurSel(0);
  249.     OnSelchangeListInputDevices();
  250.  
  251.     return hr;
  252. }
  253.  
  254. HRESULT CAudioCapDlg::GetInterfaces()
  255. {
  256.     HRESULT hr;
  257.  
  258.     // Create the filter graph.
  259.     hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
  260.                           IID_IGraphBuilder, (void **)&m_pGB);
  261.     if (!m_pGB)
  262.         return E_NOINTERFACE;
  263.  
  264.     // Create the capture graph builder.
  265.     hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, 
  266.                           IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
  267.     if (!m_pCapture)
  268.         return E_NOINTERFACE;
  269.  
  270.     // Associate the filter graph with the capture graph builder
  271.     hr = m_pCapture->SetFiltergraph(m_pGB);    
  272.     if (FAILED(hr))
  273.         return hr;
  274.  
  275.     // Get useful interfaces from the graph builder
  276.     JIF(hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
  277.     JIF(hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));
  278.  
  279.     // Have the graph signal events via window callbacks
  280.     hr = m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, RECORD_EVENT);
  281.  
  282. CLEANUP:
  283.     return hr;
  284. }
  285.  
  286. HRESULT CAudioCapDlg::GetPlaybackInterfaces()
  287. {
  288.     HRESULT hr;
  289.  
  290.     // Release any existing interface pointers
  291.     FreePlaybackInterfaces();
  292.  
  293.     // Create the filter graph.
  294.     hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
  295.                           IID_IGraphBuilder, (void **)&m_pGBPlayback);
  296.     if (!m_pGBPlayback)
  297.         return E_NOINTERFACE;
  298.  
  299.     // Get useful interfaces from the graph builder
  300.     JIF(hr = m_pGBPlayback->QueryInterface(IID_IMediaControl, (void **)&m_pMCPlayback));
  301.     JIF(hr = m_pGBPlayback->QueryInterface(IID_IMediaEventEx, (void **)&m_pMEPlayback));
  302.  
  303.     // Have the graph signal events via window callbacks
  304.     hr = m_pMEPlayback->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, PLAYBACK_EVENT);
  305.  
  306. CLEANUP:
  307.     return hr;
  308. }
  309.  
  310. void CAudioCapDlg::ClearAllocatedLists()
  311. {
  312.     // Clear the list and delete its allocated CLSID bytes
  313.     ClearFilterListWithMoniker(m_ListInputs);
  314. }
  315.  
  316. void CAudioCapDlg::ClearLists()
  317. {
  318.     // Clear the lists that don't contain extra data
  319.     m_ListInputPins.ResetContent();
  320.  
  321.     m_ListFilters.ResetContent();
  322.     m_ListFilterInputs.ResetContent();
  323.     m_ListFilterOutputs.ResetContent();
  324. }
  325.  
  326. void CAudioCapDlg::FreeInterfaces()
  327. {
  328.     // Disable event callbacks
  329.     if (m_pME)
  330.         m_pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
  331.  
  332.     // Release and zero DirectShow interface pointers
  333.     SAFE_RELEASE(m_pMC);
  334.     SAFE_RELEASE(m_pME);
  335.     SAFE_RELEASE(m_pSplitter);
  336.     SAFE_RELEASE(m_pFileWriter);
  337.     SAFE_RELEASE(m_pWAVDest);
  338.     SAFE_RELEASE(m_pInputDevice);
  339.     SAFE_RELEASE(m_pGB);
  340.     SAFE_RELEASE(m_pCapture);
  341. }
  342.  
  343. void CAudioCapDlg::FreePlaybackInterfaces()
  344. {
  345.     HRESULT hr;
  346.  
  347.     // Disable event callbacks
  348.     if (m_pMEPlayback)
  349.         hr = m_pMEPlayback->SetNotifyWindow((OAHWND)NULL, 0, 0);
  350.  
  351.     // Release and zero DirectShow interface pointers
  352.     SAFE_RELEASE(m_pMEPlayback);
  353.     SAFE_RELEASE(m_pMCPlayback);
  354.     SAFE_RELEASE(m_pGBPlayback);
  355. }
  356.  
  357. void CAudioCapDlg::ReleaseCapture()
  358. {
  359.     ClearAllocatedLists();
  360.     ClearLists();
  361.     DestroyPreviewStream();
  362.     DestroyCaptureStream();
  363.     FreeInterfaces();
  364.     FreePlaybackInterfaces();
  365. }
  366.  
  367. void CAudioCapDlg::ResetCapture()
  368. {
  369.     ClearLists();
  370.     FreeInterfaces();
  371.     GetInterfaces();
  372. }
  373.  
  374. void CAudioCapDlg::SetDefaults()
  375. {
  376.     // Zero DirectShow interface pointers (sanity check)
  377.     m_pGB = m_pGBPlayback = 0;
  378.     m_pMC = m_pMCPlayback = 0;
  379.     m_pME = m_pMEPlayback = 0;
  380.     m_pCapture = 0;
  381.     m_pInputDevice =0;
  382.     m_pSplitter = 0;
  383.     m_pRenderer =0;
  384.     m_pWAVDest=0;
  385.     m_pFileWriter=0;
  386.     m_bPausedRecording=FALSE;
  387.  
  388.     // Set default values for controls on the dialog
  389.     m_CheckAudition.SetCheck(TRUE);
  390.     m_btnProperties.EnableWindow(FALSE);
  391.     m_StrFilename.SetWindowText(DEFAULT_FILENAME);
  392. }
  393.  
  394. void CAudioCapDlg::EnableButtons(BOOL bEnable)
  395. {
  396.     m_btnRecord.EnableWindow(bEnable);
  397.     m_btnPause.EnableWindow(bEnable);
  398.     m_btnStop.EnableWindow(bEnable);
  399.     m_btnPlay.EnableWindow(bEnable);
  400. }
  401.  
  402. void CAudioCapDlg::EnableRadioButtons(BOOL bEnable)
  403. {
  404.     GetDlgItem(IDC_RADIO_11KHZ)->EnableWindow(bEnable);
  405.     GetDlgItem(IDC_RADIO_22KHZ)->EnableWindow(bEnable);
  406.     GetDlgItem(IDC_RADIO_44KHZ)->EnableWindow(bEnable);
  407.     GetDlgItem(IDC_RADIO_8BIT)->EnableWindow(bEnable);
  408.     GetDlgItem(IDC_RADIO_16BIT)->EnableWindow(bEnable);
  409.     GetDlgItem(IDC_RADIO_MONO)->EnableWindow(bEnable);
  410.     GetDlgItem(IDC_RADIO_STEREO)->EnableWindow(bEnable);
  411. }
  412.  
  413. void CAudioCapDlg::OnClose() 
  414. {
  415.     if (m_pMC)
  416.         m_pMC->Stop();
  417.  
  418.     ReleaseCapture();
  419.     CoUninitialize();
  420.  
  421.     CDialog::OnClose();
  422. }
  423.  
  424. void CAudioCapDlg::OnDestroy() 
  425. {
  426.     ReleaseCapture();
  427.     CDialog::OnDestroy();
  428. }
  429.  
  430. HRESULT CAudioCapDlg::HandleGraphEvent(void)
  431. {
  432.     LONG evCode, evParam1, evParam2;
  433.     HRESULT hr=S_OK;
  434.  
  435.     if (!m_pME)
  436.         return S_OK;
  437.  
  438.     // Read all events currently on the event queue
  439.     while(SUCCEEDED(m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, 
  440.                                     (LONG_PTR *) &evParam2, 0)))
  441.     {
  442.         if(EC_DEVICE_LOST == evCode)
  443.         {
  444.             // Device was removed
  445.             if (evParam2 == 0)
  446.             {
  447.                 OnButtonStop();
  448.                 ReleaseCapture();
  449.                 SetDefaults();
  450.                 MessageBeep(0);
  451.                 MessageBox(TEXT("The selected input device was removed!\0"),
  452.                            TEXT("DirectShow Audio Capture Sample\0"), 
  453.                            MB_OK | MB_ICONEXCLAMATION);
  454.             }
  455.             // Device was reattached
  456.             else
  457.             {
  458.                 // Restart from scratch
  459.                 InitializeCapture();
  460.                 MessageBeep(0);
  461.                 MessageBox(TEXT("The audio input device has been reattached!\0"),
  462.                            TEXT("DirectShow Audio Capture Sample\0"), 
  463.                            MB_OK | MB_ICONINFORMATION);
  464.             }
  465.         }
  466.  
  467.         // Free event parameters to prevent memory leaks
  468.         hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);
  469.     }
  470.  
  471.     return hr;
  472. }
  473.  
  474. HRESULT CAudioCapDlg::HandlePlaybackGraphEvent(void)
  475. {
  476.     LONG evCode, evParam1, evParam2;
  477.     HRESULT hr=S_OK;
  478.  
  479.     if (!m_pMEPlayback)
  480.         return S_OK;
  481.  
  482.     // Read all events currently on the event queue
  483.     while(SUCCEEDED(m_pMEPlayback->GetEvent(&evCode, (LONG_PTR *) &evParam1, 
  484.                                     (LONG_PTR *) &evParam2, 0)))
  485.     {
  486.         // Free event parameters to prevent memory leaks
  487.         hr = m_pMEPlayback->FreeEventParams(evCode, evParam1, evParam2);
  488.  
  489.         if (evCode == EC_COMPLETE)
  490.         {
  491.             OnButtonStop();
  492.             break;
  493.         }
  494.     }
  495.  
  496.     return hr;
  497. }
  498.  
  499. LRESULT CAudioCapDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  500. {
  501.     // Field notifications from the DirectShow filter graph manager
  502.     // and those posted by the application
  503.     switch (message)
  504.     {
  505.         case WM_GRAPHNOTIFY:
  506.             if (lParam == RECORD_EVENT)
  507.                 HandleGraphEvent();
  508.             else
  509.                 HandlePlaybackGraphEvent();
  510.             break;
  511.     }
  512.     
  513.     return CDialog::WindowProc(message, wParam, lParam);
  514. }
  515.  
  516. void CAudioCapDlg::OnButtonProperties() 
  517. {
  518.     // Use helper function to display property page for this device
  519.     ShowFilterPropertyPage(m_pInputDevice, m_hWnd);
  520.  
  521.     // Increment the device's reference count
  522.     m_pInputDevice->AddRef();
  523. }
  524.  
  525. void CAudioCapDlg::OnSelchangeListInputDevices() 
  526. {
  527.     HRESULT hr;
  528.     IMoniker *pMoniker=0;
  529.  
  530.     // Release and delete any previous capture filter graph
  531.     if (m_pInputDevice)
  532.         ResetCapture();
  533.  
  534.     // Get the currently selected category name
  535.     int nItem = m_ListInputs.GetCurSel();
  536.  
  537.     // Read the stored moniker pointer from the list box's item data
  538.     pMoniker = (IMoniker *) m_ListInputs.GetItemDataPtr(nItem);
  539.  
  540.     // Use the moniker to create the specified audio capture device
  541.     hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pInputDevice);   
  542.     if (FAILED(hr))
  543.         return;
  544.  
  545.     // Add the capture filter to the filter graph
  546.     hr = m_pGB->AddFilter(m_pInputDevice, L"Audio Capture");
  547.     if (FAILED(hr))
  548.         return;
  549.  
  550.     // List and display the available input pins on the capture filter.
  551.     // Many devices offer multiple pins for input (microphone, CD, LineIn, etc.)
  552.     hr = EnumPinsOnFilter(m_pInputDevice, PINDIR_INPUT, m_ListInputPins);
  553.     if (FAILED(hr))
  554.         return;
  555.  
  556.     // Enable the properties button, if supported by the selected input device
  557.     m_btnProperties.EnableWindow(SupportsPropertyPage(m_pInputDevice));
  558.  
  559.     // Select the first available input pin by default
  560.     m_ListInputPins.SetCurSel(0);
  561.     OnSelchangeListInputPins();
  562.  
  563.     // Now display the existing filters in the graph for diagnostic help
  564.     UpdateFilterLists(m_pGB);
  565. }
  566.  
  567. void CAudioCapDlg::OnSelchangeListFilters() 
  568. {
  569.     // Use a helper function to display the filter's pins in listboxes
  570.     if (m_pGBPlayback)
  571.     {
  572.         AddFilterPinsToLists(m_pGBPlayback, m_ListFilters, 
  573.                              m_ListFilterInputs, m_ListFilterOutputs);
  574.     }
  575.     else
  576.     {
  577.         AddFilterPinsToLists(m_pGB, m_ListFilters, 
  578.                              m_ListFilterInputs, m_ListFilterOutputs);
  579.     }
  580. }
  581.  
  582. void CAudioCapDlg::UpdateFilterLists(IGraphBuilder *pUpdateGB)
  583. {
  584.     // Now display the existing filters in the graph for diagnostic help
  585.     AddGraphFiltersToList(pUpdateGB, m_ListFilters);
  586.  
  587.     // Refresh the filter/pin display lists
  588.     m_ListFilters.SetCurSel(0);
  589.     OnSelchangeListFilters();
  590. }
  591.  
  592. void CAudioCapDlg::OnButtonPlay() 
  593. {
  594.     USES_CONVERSION;
  595.     TCHAR szPlayFile[MAX_PATH];
  596.  
  597.     Say(TEXT("Playing specified file..."));
  598.     m_StrFilename.GetWindowText(szPlayFile, MAX_PATH);
  599.  
  600.     // Play the media file using DirectShow methods
  601.     if (SUCCEEDED(GetPlaybackInterfaces()))
  602.     {
  603.         HRESULT hr = m_pGBPlayback->RenderFile(T2W(szPlayFile), NULL);
  604.         if (SUCCEEDED(hr))
  605.         {
  606.             // Update the graph filter display to show rendered graph
  607.             UpdateFilterLists(m_pGBPlayback);
  608.  
  609.             hr = m_pMCPlayback->Run();
  610.         }
  611.     }
  612.  
  613.     // Disable pause button to remove need for extra pause/play handling code
  614.     m_btnPause.EnableWindow(FALSE);
  615. }
  616.  
  617. void CAudioCapDlg::OnButtonRecord() 
  618. {
  619.     HRESULT hr;
  620.  
  621.     // Don't rebuild the graphs if we're returning from paused state
  622.     if (m_bPausedRecording)
  623.     {
  624.         if (m_pMC)
  625.             hr = m_pMC->Run();
  626.  
  627.         m_bPausedRecording = FALSE;
  628.         Say(TEXT("Auditioning/recording audio..."));
  629.         return;
  630.     }
  631.  
  632.     // If we're currently playing back the recorded file, stop playback
  633.     // and clear the interfaces before beginning recording.
  634.     FreePlaybackInterfaces();
  635.  
  636.     if (m_CheckAudition.GetCheck())
  637.         RenderPreviewStream();
  638.  
  639.     if (m_CheckWriteFile.GetCheck())
  640.         RenderCaptureStream();
  641.  
  642.     // Update the graph filter display to show rendered graph
  643.     UpdateFilterLists(m_pGB);
  644.  
  645.     if (m_pMC)
  646.     {
  647.         hr = m_pMC->Run();
  648.         Say(TEXT("Auditioning/recording audio..."));
  649.     }
  650.  
  651.     m_btnRecord.EnableWindow(FALSE);
  652.     m_btnPlay.EnableWindow(FALSE);
  653.     m_btnPause.EnableWindow(TRUE);
  654.     EnableRadioButtons(FALSE);
  655. }
  656.  
  657. void CAudioCapDlg::OnButtonPause() 
  658. {
  659.     HRESULT hr;
  660.  
  661.     // We share the functionality of the Pause button with both
  662.     // Record and Playback.  If the Playback interfaces aren't NULL,
  663.     // then we're in the middle of playback, so pause playback.
  664.     // Otherwise, pause recording.
  665.     if (m_pMCPlayback)
  666.     {
  667.         hr = m_pMCPlayback->Pause();        
  668.         Say(TEXT("Paused audio playback"));
  669.     }
  670.     else
  671.     {
  672.         if (m_pMC)
  673.         {
  674.             hr = m_pMC->Pause();
  675.             Say(TEXT("Paused audio recording"));
  676.             m_bPausedRecording = TRUE;
  677.         }
  678.     }
  679.  
  680.     m_btnRecord.EnableWindow(TRUE);
  681. }
  682.  
  683. void CAudioCapDlg::OnButtonStop() 
  684. {
  685.     HRESULT hr;
  686.  
  687.     // We share the functionality of the Stop button with both
  688.     // Record and Playback.  If the Playback interfaces aren't NULL,
  689.     // then we're in the middle of playback, so stop playback.
  690.     // Otherwise, stop recording.
  691.     if (m_pMCPlayback)
  692.     {
  693.         // Stop playback
  694.         if (m_pMCPlayback)
  695.             hr = m_pMCPlayback->Stop();
  696.  
  697.         FreePlaybackInterfaces();
  698.  
  699.         // Clear filter/pins list boxes, since there is no active filter graph
  700.         m_ListFilters.ResetContent();
  701.         m_ListFilterInputs.ResetContent();
  702.         m_ListFilterOutputs.ResetContent();
  703.     }
  704.     else
  705.     {
  706.         // Stop recording
  707.         if (m_pMC)
  708.             hr = m_pMC->StopWhenReady();
  709.  
  710.         // Destroy the existing filter graph
  711.         DestroyPreviewStream();
  712.         DestroyCaptureStream();
  713.         m_bPausedRecording = FALSE;
  714.  
  715.         // Update the graph filter display to show rendered graph
  716.         UpdateFilterLists(m_pGB);
  717.  
  718.         EnableRadioButtons(TRUE);
  719.     }
  720.  
  721.     EnableButtons(TRUE);
  722.     Say(TEXT("Ready"));
  723. }
  724.  
  725. HRESULT CAudioCapDlg::RenderCaptureStream()
  726. {
  727.     USES_CONVERSION;
  728.     HRESULT hr=0;
  729.     IFileSinkFilter2 *pFileSink;
  730.  
  731.     // Create the WAVDest filter
  732.     hr = CoCreateInstance(CLSID_WavDest, NULL, CLSCTX_INPROC,
  733.                           IID_IBaseFilter, (void **)&m_pWAVDest);
  734.     if (FAILED(hr))
  735.     {
  736.         MessageBox(TEXT("Couldn't create a WAVDest filter.  This sample filter is installed ")
  737.                    TEXT("with the DirectX 8.0 SDK in the DirectShow\\Filters\\WavDest ")
  738.                    TEXT("directory.\n\nPlease build this filter (with Visual C++) and ")
  739.                    TEXT("register it by running 'Tools->Register Control' once the ")
  740.                    TEXT("filter finishes building.\nThis helper filter is used ")
  741.                    TEXT("when writing .WAV files to disk, and this sample requires it.\n\n")
  742.                    TEXT("You may still audition the audio input, but you will not be ")
  743.                    TEXT("able to write the data to a file."),
  744.                    TEXT("Error loading WAVDest helper filter!"));
  745.         return E_NOINTERFACE; 
  746.     }
  747.  
  748.     // Create the FileWriter filter
  749.     hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC,
  750.                           IID_IFileSinkFilter2, (void **)&pFileSink);
  751.     if (FAILED(hr))
  752.         return E_NOINTERFACE; 
  753.  
  754.     // Get the file sink interface from the File Writer
  755.     hr = pFileSink->QueryInterface(IID_IBaseFilter, (void **)&m_pFileWriter);
  756.  
  757.     // Add the WAVDest filter to the graph
  758.     hr = m_pGB->AddFilter(m_pWAVDest, L"WAV Dest");
  759.     if (FAILED(hr))
  760.         return hr;
  761.  
  762.     // Add the FileWriter filter to the graph
  763.     hr = m_pGB->AddFilter((IBaseFilter *)m_pFileWriter, L"File Writer");
  764.     if (FAILED(hr))
  765.         return hr;
  766.  
  767.     // Set filter to always overwrite the file
  768.     hr = pFileSink->SetMode(AM_FILE_OVERWRITE);
  769.  
  770.     // Set the output filename, which must be a wide string
  771.     TCHAR szEditFile[MAX_PATH];
  772.     WCHAR wszFilename[MAX_PATH];
  773.  
  774.     m_StrFilename.GetWindowText(szEditFile, MAX_PATH);
  775.     if (szEditFile[0] == TEXT('\0'))
  776.         wcscpy(wszFilename, T2W(DEFAULT_FILENAME));
  777.     else
  778.         wcscpy(wszFilename, T2W(szEditFile));
  779.  
  780.     hr = pFileSink->SetFileName(wszFilename, NULL);
  781.     if (FAILED(hr))
  782.         return hr; 
  783.  
  784.     // Get the pin interface for the capture pin
  785.     IPin *pPin=0;
  786.     if (m_pSplitter)
  787.         hr = GetPin(m_pSplitter, PINDIR_OUTPUT, 0, &pPin);
  788.     else
  789.         hr = GetPin(m_pInputDevice, PINDIR_OUTPUT, 0, &pPin);
  790.  
  791.     // Connect the new filters
  792.     if (pPin)
  793.     {
  794.         hr = m_pGB->Render(pPin);
  795.         pPin->Release();
  796.     }
  797.  
  798.     // Release the FileSinkFilter2 interface, since it's no longer needed
  799.     SAFE_RELEASE(pFileSink);
  800.     return hr;
  801. }
  802.  
  803. HRESULT CAudioCapDlg::DestroyCaptureStream()
  804. {
  805.     HRESULT hr=0;
  806.  
  807.     // Destroy Audio renderer filter, if it exists
  808.     if (m_pWAVDest)
  809.     {
  810.         hr = m_pGB->RemoveFilter(m_pWAVDest);
  811.         SAFE_RELEASE(m_pWAVDest);
  812.     }
  813.  
  814.     // Destroy Smart Tee filter, if it exists
  815.     if (m_pFileWriter)
  816.     {
  817.         hr = m_pGB->RemoveFilter(m_pFileWriter);
  818.         SAFE_RELEASE(m_pFileWriter);
  819.     }
  820.  
  821.     return hr;
  822. }
  823.  
  824. HRESULT CAudioCapDlg::RenderPreviewStream()
  825. {
  826.     USES_CONVERSION;
  827.     HRESULT hr=0;
  828.     WCHAR wszFilter[64];
  829.  
  830.     // If we've already configured the stream, just exit
  831.     if (m_pRenderer)
  832.         return S_OK;
  833.  
  834.     // Set the requested audio properties - buffer sizes, channels, freq, etc.
  835.     hr = SetAudioProperties();
  836.  
  837.     // Render the preview stream
  838.     hr = m_pCapture->RenderStream(
  839.             &PIN_CATEGORY_PREVIEW, 
  840.             &MEDIATYPE_Audio, 
  841.             m_pInputDevice, 
  842.             NULL,   // No compression filter.
  843.             NULL    // Default renderer.
  844.         );
  845.  
  846.     // Some capture sources will have only a Capture pin (no Preview pin).
  847.     // In that case, the Capture Graph builder will automatically insert
  848.     // a SmartTee filter, which will split the stream into a Capture stream
  849.     // and a Preview stream.  In that case, it's not an error.
  850.     if (hr == VFW_S_NOPREVIEWPIN)
  851.     {
  852.         wcscpy(wszFilter, T2W(TEXT("Smart Tee")));
  853.  
  854.         // Get the interface for the Splitter filter for later use
  855.         hr = m_pGB->FindFilterByName(wszFilter, &m_pSplitter);
  856.     }
  857.  
  858.     // Get the interface for the audio renderer filter for later use
  859.     wcscpy(wszFilter, T2W(TEXT("Audio Renderer")));
  860.     hr = m_pGB->FindFilterByName(wszFilter, &m_pRenderer);   
  861.  
  862.     return hr;
  863. }
  864.  
  865. HRESULT CAudioCapDlg::DestroyPreviewStream()
  866. {
  867.     HRESULT hr=0;
  868.  
  869.     // Destroy Smart Tee filter, if it exists
  870.     if (m_pSplitter)
  871.     {
  872.         hr = m_pGB->RemoveFilter(m_pSplitter);
  873.         SAFE_RELEASE(m_pSplitter);
  874.     }
  875.  
  876.     // Destroy Audio renderer filter, if it exists
  877.     if (m_pRenderer)
  878.     {
  879.         hr = m_pGB->RemoveFilter(m_pRenderer);
  880.         SAFE_RELEASE(m_pRenderer);
  881.     }
  882.  
  883.     return hr;
  884. }
  885.  
  886. void CAudioCapDlg::OnSelchangeListInputPins() 
  887. {
  888.     HRESULT hr;
  889.  
  890.     // Make sure that playback is stopped
  891.     OnButtonStop();
  892.  
  893.     // Select the capture device's input pin
  894.     hr = ActivateSelectedInputPin();
  895. }
  896.  
  897. HRESULT CAudioCapDlg::ActivateSelectedInputPin() 
  898. {
  899.     HRESULT hr=S_OK;
  900.     IPin *pPin=0;
  901.     IAMAudioInputMixer *pPinMixer;
  902.  
  903.     // How many pins are in the input pin list?
  904.     int nPins = m_ListInputPins.GetCount();
  905.     int nActivePin = m_ListInputPins.GetCurSel();
  906.  
  907.     // Activate the selected input pin and deactivate all others
  908.     for (int i=0; i<nPins; i++)
  909.     {
  910.         // Get this pin's interface
  911.         hr = GetPin(m_pInputDevice, PINDIR_INPUT, i, &pPin);
  912.         if (SUCCEEDED(hr))
  913.         {
  914.             hr = pPin->QueryInterface(IID_IAMAudioInputMixer, (void **)&pPinMixer);
  915.             if (SUCCEEDED(hr))
  916.             {
  917.                 // If this is our selected pin, enable it
  918.                 if (i == nActivePin)
  919.                 {
  920.                     // Set any other audio properties on this pin
  921.                     hr = SetInputPinProperties(pPinMixer);
  922.  
  923.                     // If there is only one input pin, this method
  924.                     // might return E_NOTIMPL.
  925.                     hr = pPinMixer->put_Enable(TRUE);
  926.                 }
  927.                 // Otherwise, disable it
  928.                 else
  929.                 {
  930.                     hr = pPinMixer->put_Enable(FALSE);
  931.                 }
  932.  
  933.                 pPinMixer->Release();
  934.             }
  935.  
  936.             // Release pin interfaces
  937.             pPin->Release();
  938.         }
  939.     }
  940.  
  941.     return hr;
  942. }
  943.  
  944. HRESULT CAudioCapDlg::SetInputPinProperties(IAMAudioInputMixer *pPinMixer) 
  945. {
  946.     HRESULT hr=0;
  947.     BOOL bLoudness=0, bMono=0;
  948.     double dblBass=0, dblBassRange=0, dblMixLevel=0, 
  949.            dblPan=0, dblTreble=0, dblTrebleRange=0;
  950.  
  951.     // Read current settings for debugging purposes.  Many of these interfaces
  952.     // will not be implemented by the input device's driver, so they will
  953.     // return E_NOTIMPL.  In that case, just ignore the values.
  954.     hr = pPinMixer->get_Bass(&dblBass);
  955.     hr = pPinMixer->get_BassRange(&dblBassRange);
  956.     hr = pPinMixer->get_Loudness(&bLoudness);
  957.     hr = pPinMixer->get_MixLevel(&dblMixLevel);
  958.     hr = pPinMixer->get_Mono(&bMono);
  959.     hr = pPinMixer->get_Pan(&dblPan);
  960.     hr = pPinMixer->get_Treble(&dblTreble);
  961.     hr = pPinMixer->get_TrebleRange(&dblTrebleRange);
  962.  
  963.     // Manipulate desired values here (mono/stereo, pan, etc.)
  964.  
  965.     return hr;    
  966. }
  967.  
  968. void CAudioCapDlg::Say(TCHAR *szMsg) 
  969. {
  970.     // Display status information on the main dialog
  971.     m_strStatus.SetWindowText(szMsg);
  972. }
  973.  
  974. HRESULT CAudioCapDlg::SetAudioProperties()
  975. {
  976.     HRESULT hr=0;
  977.     IPin *pPin=0;
  978.     IAMBufferNegotiation *pNeg=0;
  979.     IAMStreamConfig *pCfg=0;
  980.     int nFrequency;
  981.  
  982.     // Determine audio properties
  983.     int nChannels = m_btnMono.GetCheck() ? 1 : 2;
  984.     int nBytesPerSample = m_btn8BIT.GetCheck() ? 1 : 2;
  985.  
  986.     if (IsDlgButtonChecked(IDC_RADIO_11KHZ))      nFrequency = 11025;
  987.     else if (IsDlgButtonChecked(IDC_RADIO_22KHZ)) nFrequency = 22050;
  988.     else nFrequency = 44100;
  989.  
  990.     // Find number of bytes in one second
  991.     long lBytesPerSecond = (long) (nBytesPerSample * nFrequency * nChannels);
  992.  
  993.     // Set to 50ms worth of data    
  994.     long lBufferSize = (long) ((float) lBytesPerSecond * DEFAULT_BUFFER_TIME);
  995.  
  996.     // Get the buffer negotiation interface for each pin,
  997.     // since there could be both a Capture and a Preview pin.
  998.     for (int i=0; i<2; i++)
  999.     {
  1000.         hr = GetPin(m_pInputDevice, PINDIR_OUTPUT, i, &pPin);
  1001.         if (SUCCEEDED(hr))
  1002.         {
  1003.             // Get buffer negotiation interface
  1004.             hr = pPin->QueryInterface(IID_IAMBufferNegotiation, (void **)&pNeg);
  1005.             if (FAILED(hr))
  1006.             {
  1007.                 pPin->Release();
  1008.                 break;
  1009.             }
  1010.  
  1011.             // Set the buffer size based on selected settings
  1012.             ALLOCATOR_PROPERTIES prop={0};
  1013.             prop.cbBuffer = lBufferSize;
  1014.             prop.cBuffers = 6;
  1015.             prop.cbAlign = nBytesPerSample * nChannels;
  1016.             hr = pNeg->SuggestAllocatorProperties(&prop);
  1017.             pNeg->Release();
  1018.  
  1019.             // Now set the actual format of the audio data
  1020.             hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pCfg);
  1021.             if (FAILED(hr))
  1022.             {
  1023.                 pPin->Release();
  1024.                 break;
  1025.             }            
  1026.  
  1027.             // Read current media type/format
  1028.             AM_MEDIA_TYPE *pmt={0};
  1029.             hr = pCfg->GetFormat(&pmt);
  1030.  
  1031.             if (SUCCEEDED(hr))
  1032.             {
  1033.                 // Fill in values for the new format
  1034.                 WAVEFORMATEX *pWF = (WAVEFORMATEX *) pmt->pbFormat;
  1035.                 pWF->nChannels = (WORD) nChannels;
  1036.                 pWF->nSamplesPerSec = nFrequency;
  1037.                 pWF->nAvgBytesPerSec = lBytesPerSecond;
  1038.                 pWF->wBitsPerSample = (WORD) (nBytesPerSample * 8);
  1039.                 pWF->nBlockAlign = (WORD) (nBytesPerSample * nChannels);
  1040.  
  1041.                 // Set the new formattype for the output pin
  1042.                 hr = pCfg->SetFormat(pmt);
  1043.                 DeleteMediaType(pmt);
  1044.             }
  1045.  
  1046.             // Release interfaces
  1047.             pCfg->Release();
  1048.             pPin->Release();
  1049.         }
  1050.         // No more output pins on this filter
  1051.         else
  1052.             break;
  1053.     }
  1054.  
  1055.     return hr;
  1056. }
  1057.